package com.yizhuoyan.idea.ucc;

import java.io.CharArrayWriter;
import java.io.IOException;
import java.io.InputStream;
import java.util.Objects;

/**
 * 类似base64的unicode中文262143编码器
 * 每14位编码为一个unicode中文，每4个字符可表示7个字节（56位=14x4=8x7）
 *
 * 编码后比之前大0.14453125
 */
public class UnicodeChineseEncoder {
	/**
	 * 特殊编码字符，用于编码无法分组剩余6个的情况
	 */
	static final char REMAINDER6_LAST_CHAR = 0x4e01 + (~(-1 << 14));

	public static void encode(InputStream inputStream, CharArrayWriter charArrayWriter) throws IOException {
		byte[] bytes7 = new byte[7];
		int readed = 0;
		while ((readed = inputStream.read(bytes7)) != -1) {
			charArrayWriter.write(encode(bytes7, 0, readed));
		}
		charArrayWriter.flush();
	}

	public static char[] encode(InputStream inputStream) throws IOException {
		CharArrayWriter charArrayWriter = new CharArrayWriter();
		encode(inputStream, charArrayWriter);
		return charArrayWriter.toCharArray();
	}

	public static char[] encode(byte[] src) {
		return encode(src, 0, src.length);
	}

	/**
	 * 执行编码
	 *
	 * @param src 目前字节
	 * @return 如果为null或空，则返回空
	 */
	public static char[] encode(byte[] src, int off, int len) {
		if (src == null || src.length == 0) {
			return new char[0];
		}
		Objects.checkFromIndexSize(off, len, src.length);
		if (len == 0) {
			return new char[0];
		}
		int rstLen = rstLength(len);          // dst array size
		char[] dst = new char[rstLen];
		encode0(src, off, len, dst);
		return dst;
	}

	private static int rstLength(int src) {
		int len = (src / 7) * 4;
		switch (src % 7) {
			case 0:
				return len;
			case 1:
				return len + 1;
			case 2:
			case 3:
				return len + 2;
			case 4:
			case 5:
				return len + 3;
			case 6:
				return len + 4;
		}
		throw new UnknownError();
	}

	private static void encode0(byte[] src, int i, int len, char[] dst) {
		int remainder = len % 7;
		int stop = len - remainder;
		int j = 0;
		while (i < stop) {
			long bits = (src[i++] & 0xffL) << 48 |
					(src[i++] & 0xffL) << 40 |
					(src[i++] & 0xffL) << 32 |
					(src[i++] & 0xffL) << 24 |
					(src[i++] & 0xffL) << 16 |
					(src[i++] & 0xffL) << 8 |
					(src[i++] & 0xffL);
			dst[j++] = (char) (((bits >>> 42) & 0x3fff) + 0x4e00);
			dst[j++] = (char) (((bits >>> 28) & 0x3fff) + 0x4e00);
			dst[j++] = (char) (((bits >>> 14) & 0x3fff) + 0x4e00);
			dst[j++] = (char) (((bits) & 0x3fff) + 0x4e00);
		}
		switch (remainder) {
			case 0:
				return;
			case 1://8
				//000-000 00 xxxxxxxx
				dst[j] = (char) (src[i] + 0x4e00);
				break;
			case 2: //16 14+2
			{
				//000-00 00 000000xx0
				int remainder2Bits = ((src[i++] & 0xff) << 8) | (src[i] & 0xff);
				dst[j++] = (char) (((remainder2Bits >> 2) & 0x3fff) + 0x4e00);
				dst[j] = (char) (((remainder2Bits & 0x3) << 1) + 0x4e00);
				break;
			}
			case 3: //24 14+10
			{
				int remainder3Bits = ((src[i++] & 0xff) << 16) | ((src[i++] & 0xff) << 8) | (src[i] & 0xff);
				dst[j++] = (char) (((remainder3Bits >> 10) & 0x3fff) + 0x4e00);
				//000-0-0xxx xxxxxxx1
				dst[j] = (char) (((remainder3Bits & 0x3ff) << 1) + 0x4e01);
				break;
			}
			case 4: //32 14+14+4
			{
				int remainder4Bits = ((src[i++] & 0xff) << 24) | ((src[i++] & 0xff) << 16) | ((src[i++] & 0xff) << 8) | (src[i] & 0xff);
				dst[j++] = (char) (((remainder4Bits >> 18) & 0x3fff) + 0x4e00);
				dst[j++] = (char) (((remainder4Bits >> 4) & 0x3fff) + 0x4e00);
				//000-00-000 000xxxx0
				dst[j] = (char) (((remainder4Bits & 0xf) << 1) + 0x4e00);
				break;
			}
			case 5: //40 14+14+4+8
			{
				int remainder4Bits = ((src[i++] & 0xff) << 24) | ((src[i++] & 0xff) << 16) | ((src[i++] & 0xff) << 8) | (src[i++] & 0xff);
				int remainder1Bits = (src[i] & 0xff);
				dst[j++] = (char) (((remainder4Bits >> 18) & 0x3fff) + 0x4e00);
				dst[j++] = (char) (((remainder4Bits >> 4) & 0x3fff) + 0x4e00);
				//??0-x-xxxx xxxxxxx1
				dst[j] = (char) ((((remainder4Bits & 0xf) << 9) | (remainder1Bits << 1)) + 0x4e01);
				break;
			}
			case 6: //48 14+14+(4+10)+6
			{
				int remainder4Bits = ((src[i++] & 0xff) << 24) | ((src[i++] & 0xff) << 16) | ((src[i++] & 0xff) << 8) | (src[i++] & 0xff);
				int remainder2Bits = ((src[i++] & 0xff) << 8) | (src[i] & 0xff);
				dst[j++] = (char) (((remainder4Bits >> 18) & 0x3fff) + 0x4e00);
				dst[j++] = (char) (((remainder4Bits >> 4) & 0x3fff) + 0x4e00);
				dst[j++] = (char) ((((remainder4Bits & 0xf)<<10) | ((remainder2Bits >> 6) & 0x3ff)) + 0x4e00);
				//special deal
				dst[j] = (char) ((remainder2Bits & 0x3f) + REMAINDER6_LAST_CHAR);
				break;
			}
		}
	}


}
